package cn.wps.moffice.util;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Comparator;
import java.util.Random;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 文件名 ： StringUtil.java
 * 创建者 ： 朱健
 * 维护人 ： 朱健
 * 创建时间 ： 2011-2-18 14:08:48
 * 功能描述 ： 处理字符串操作
 */
public class StringUtil
{
    public static boolean isEmpty(String s)
    {
        return s == null || s.trim().length() == 0;
    }

    private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
    private static final String ROOT_PATH  = "/";

    /**
     * 判断String数组包含某一string
     * 
     * @param strs
     * @param str
     * @return
     */
    public static boolean isHave(String[] strs, String str)
    {
    	if (strs == null || str == null)
    	{
    		return false;
    	}
        for (int i = 0; i < strs.length; i++)
        {
            if (str.equals(strs[i]))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断str以string数组某一项开头
     * 
     * @param strs
     * @param str
     * @return
     */
    public static boolean isStartWith(String[] strs, String str)
    {
        if (str == null || strs == null)
        {
            return false;
        }
        for (int i = 0; i < strs.length; i++)
        {
            if (str.startsWith(strs[i]))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * 解析一个带 token 分隔符的字符串，这个方法的效率比直接调用String的split()方法快大约1倍。
     * 
     * @param tokenedStr
     * @param token
     * @return String[]
     */
    public static String[] splitString(String tokenedStr, String token)
    {
        String[] ids = null;
        if (tokenedStr != null)
        {
            StringTokenizer st = new StringTokenizer(tokenedStr, token);
            final int arraySize = st.countTokens();
            if (arraySize > 0)
            {
                ids = new String[arraySize];
                int counter = 0;
                while (st.hasMoreTokens())
                {
                    ids[counter++] = st.nextToken();
                }
            }
        }
        return ids;
    }

    /**
     * 把字符串数组组合成一个以指定分隔符分隔的字符串，并追加到给定的<code>StringBuilder</code>
     * 
     * @param strs
     *            字符串数组
     * @param seperator
     *            分隔符
     * @return
     */
    public static void mergeString(String[] strs, String seperator, StringBuilder sb)
    {
        if (strs == null || strs.length == 0)
            return;
        for (int i = 0; i < strs.length; i++)
        {
            if (i != 0)
            {
                sb.append(seperator);
            }
            sb.append(strs[i]);
        }
    }

    public static String stringWithFormat(String str, Object... args)
    {
        str = String.format(str, args);
        return str;
    }

    /**
     * 把字符串数组组合成一个以指定分隔符分隔的字符串。
     * 
     * @param strs
     *            字符串数组
     * @param seperator
     *            分隔符
     * @return
     */
    public static String mergeString(String[] strs, String seperator)
    {
        StringBuilder sb = new StringBuilder();
        mergeString(strs, seperator, sb);
        return sb.toString();
    }

    /**
     * MessageEncrypt 文件后缀名小写
     * 
     * @param strFileName
     * @return
     */
    public static String lowerCaseExtension(String strFileName)
    {
        // 去掉文件后缀
        String strFileNameBody = stringByDeletingPathExtension(strFileName);
        // 得到文件后缀
        String strExt = pathExtension(strFileName).toLowerCase();
        strFileName = strFileNameBody + "." + strExt;
        return strFileName;
    }

    /**
     * 得到文件的类型。 实际上就是得到文件名中最后一个“.”后面的部分。
     * 
     * @param fileName
     *            文件名
     * @return 文件名中的类型部分
     */
    public static String pathExtension(String fileName)
    {
        int point = fileName.lastIndexOf('.');
        int length = fileName.length();
        if (point == -1 || point == length - 1)
        {
            return "";
        }
        else
        {
            return fileName.substring(point + 1, length);
        }
    }

    /**
     * 将文件名中的类型部分去掉。不含点
     * 
     * @param filename
     *            文件名
     * @return 去掉类型部分的结果
     */
    public static String stringByDeletingPathExtension(String filename)
    {
        int index = filename.lastIndexOf(".");
        if (index != -1)
        {
            return filename.substring(0, index);
        }
        else
        {
            return filename;
        }
    }

    /**
     * strFolder/filename
     * 
     * @param strFolder
     * @param filename
     * @return
     */
    public static String stringByAppendingPathComponent(String strFolder, String filename)
    {
        return strFolder + File.separator + filename;
    }

    /**
     * trimExt.ext
     * 
     * @param trimExt
     * @param ext
     * @return
     */
    public static String stringByAppendingPathExtension(String trimExt, String ext)
    {
        return trimExt + "." + ext;
    }

    /**
     * 得到文件的名字部分。
     * 实际上就是路径中的最后一个路径分隔符后的部分。
     * 
     * @param fileName
     *            文件名
     * @return 文件名中的名字部分
     * @since 0.5
     */
    public static String getNamePart(String fileName)
    {
    	if(null == fileName){//for user feed back getPathLastIndex NullPointerException
    		return "";
    	}
        int point = getPathLastIndex(fileName);
        int length = fileName.length();
        if (point == -1)
        {
            return fileName;
        }
        else if (point == length - 1)
        {
            int secondPoint = getPathLastIndex(fileName, point - 1);
            if (secondPoint == -1)
            {
                if (length == 1)
                {
                    return fileName;
                }
                else
                {
                    return fileName.substring(0, point);
                }
            }
            else
            {
                return fileName.substring(secondPoint + 1, point);
            }
        }
        else
        {
            return fileName.substring(point + 1);
        }
    }
    
    public static String getNameWithoutSuffix(String filePath)
    {
    	if(null == filePath || filePath.length() == 0){//for user feed back getPathLastIndex NullPointerException
    		return "";
    	}
    	
    	int separatorIndex = filePath.lastIndexOf(File.separator);
    	int beginIndex = 0;
    	int endIndex = filePath.length();
    	if (separatorIndex >= 0)
    	{
    		beginIndex = separatorIndex + 1;
    	}
         
    	final int suffixPos = filePath.lastIndexOf('.');
    	if (suffixPos > 0 && suffixPos < filePath.length() - 1)
    	{
    		endIndex = suffixPos;
    	}
    	
    	return filePath.substring(beginIndex, endIndex);
    }

    /**
     * 得到除去文件名部分的路径
     * 实际上就是路径中的最后一个路径分隔符前的部分。
     * 
     * @param fileName
     * @return
     */
    public static String getNameDelLastPath(String fileName)
    {
        if (fileName == null)
            return fileName;
        int point = getPathLastIndex(fileName);
        if (point == -1)
        {
            return fileName;
        }
        else
        {
            return fileName.substring(0, point);
        }
    }

    /**
     * 得到路径分隔符在文件路径中最后出现的位置。
     * 对于DOS或者UNIX风格的分隔符都可以。
     * 
     * @param fileName
     *            文件路径
     * @return 路径分隔符在路径中最后出现的位置，没有出现时返回-1。
     * @since 0.5
     */
    public static int getPathLastIndex(String fileName)
    {
    	if(null == fileName){//for user feed back getPathLastIndex NullPointerException
    		return -1;
    	}
        int point = fileName.lastIndexOf('/');
        if (point == -1)
        {
            point = fileName.lastIndexOf('\\');
        }
        return point;
    }

    /**
     * 得到路径分隔符在文件路径中指定位置前最后出现的位置。
     * 对于DOS或者UNIX风格的分隔符都可以。
     * 
     * @param fileName
     *            文件路径
     * @param fromIndex
     *            开始查找的位置
     * @return 路径分隔符在路径中指定位置前最后出现的位置，没有出现时返回-1。
     * @since 0.5
     */
    public static int getPathLastIndex(String fileName, int fromIndex)
    {
    	if(null == fileName){//for user feed back getPathLastIndex NullPointerException
    		return -1;
    	}
        int point = fileName.lastIndexOf('/', fromIndex);
        if (point == -1)
        {
            point = fileName.lastIndexOf('\\', fromIndex);
        }
        return point;
    }

    public static final String toString(byte[] ba)
    {
        return toString(ba, 0, ba.length);
    }

    public static final String toString(byte[] ba, int offset, int length)
    {
        char[] buf = new char[length * 2];
        for (int i = 0, j = 0, k; i < length;)
        {
            k = ba[offset + i++];
            buf[j++] = HEX_DIGITS[(k >>> 4) & 0x0F];
            buf[j++] = HEX_DIGITS[k & 0x0F];
        }
        return new String(buf);
    }

    /**
     * 过滤特殊字符
     * 
     * @param str
     * @return
     */
    public static String removeInvalidChar(String str)
    {
        // 清除掉所有特殊字符
        String regEx = "[`~!@#$%^&*()+=|{}':;＇,\\[\\].<>/?～！＠＃￥％……＆＊（）——＋｜｛｝【】‘；：”“’。，、？]";
        Pattern p = Pattern.compile(regEx);
        Matcher m = p.matcher(str);
        return m.replaceAll("").trim();
    }

    public static class StringComparator implements Comparator<String>
    {
        @Override
        public int compare(String object1, String object2)
        {
            return object1.compareTo(object2);
        }
    }

    /**
     * 生成size位随机数
     * 
     * @param size
     * @return
     */
    public static String createRandomKey(int size)
    {
        char[] c = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' };
        Random random = new Random(); // 初始化随机数产生器
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < size; i++)
        {
            int r = random.nextInt();
            if (r == Integer.MIN_VALUE)
            	r = 0;
            
			sb.append(c[Math.abs(r) % c.length]);
        }
        return sb.toString();
    }

    /**
     * 根据文档大小生成文档大小的字符
     * 
     * @param size
     *            文档大小
     * @return 文档大小的字符
     */
    public static String stringFromSize(double size)
    {
        if (size < 0)
            return "-";

        double B = size;
        if(B == 0)
        {
        	return "0B";
        }

        double K = size / 1024.0;
        if(K < 1024.0)
        {
        	if(K < 1.0)
        	{
        		if(K < 0.1)
        		{
        			K = 0.1;
        		}
        		return StringUtil.stringWithFormat("%.1fKB", K);
        	}
        	else
        	{
        		int k = (int) K;
        		return StringUtil.stringWithFormat("%dKB", k);
        	}
        }

        double M = K / 1024.0;
        if (M < 1024.0)
            return StringUtil.stringWithFormat("%.1fMB", M);

        double G = M / 1024.0;
        return StringUtil.stringWithFormat("%.1fGB", G);
    }

    /**
     * 得到文件的父文件夹。
     * 例如/mnt/sdcard/a.doc,得到sdcard。特殊情况：根目录/下的文件得到的是/
     * 
     * @param 文件绝对路径
     * @return 文件的父文件夹名字
     */
    public static String getFileNameSubStr(String path)
    {
        path = path.substring(0, path.lastIndexOf('/') + 1);
        if (ROOT_PATH.equals(path))
        {
            return path;
        }
        if (path.charAt(path.length() - 1) == '/')
            path = path.substring(0, path.length() - 1);
        return path.substring(path.lastIndexOf('/') + 1);
        // File f = new File(path);
        // String parentPath = f.getParent();
        // return getNamePart(parentPath);
    }

    /**
     * 获取字符串tag第N次在字符串text出现的下标
     * 
     * @param text
     *            字符串
     * @param tag
     *            待查询字符串
     * @param flag
     *            字符串第N次出现
     * @author longzihang
     */
    public static int search(String text, String tag, int flag)
    {
        int tagLength = tag.length();// 搜索的字符或者字符串长度
        String[] temp = text.split(tag);// 拆分text
        int l = temp.length;// text拆分后得到的数组长度
        // 如果text不是以tag结束，将l的值-1
        if (!text.endsWith(tag))
        {
            l = l - 1;
        }
        // 其实位置
        int position = 0;
        int[] positionarr = new int[l];
        for (int i = 0; i < l; i++)
        {
            String s = temp[i];
            position += s.length();
            positionarr[i] = position;
            position += tagLength;
        }
        return positionarr[flag - 1];
    }

    /**
     * Normalize a path, e.g. resolve ".", "..", "//"
     */
    public static String normalizePath(String path)
    {
    	if (!isEmpty(path))
		{
    		if (path.contains("//")) //暂时仅处理多出 '/'的问题
    		{
    			path = path.replaceAll("/+/", "/");
    		}//else do nothing.
		}
        
        return path;
    }

    /**
     * All strings will be encoded as UTF-8.
     */
    public static final String  DEFAULT_CHARSET = "UTF-8";

    /**
     * One-way hashing function used for providing a checksum of data
     */
    private static final String HASH_ALGORITHM  = "MD5";

    /**
     * A runtime exception that will be thrown when we hit an error that should
     * "never" occur ... e.g. if the JVM doesn't know about UTF-8 or MD5.
     */
    private static final class StringUtilException extends RuntimeException
    {
        private static final long serialVersionUID = -8099786694856724498L;

        public StringUtilException(String message, Throwable cause)
        {
            super(message, cause);
        }
    }

    /**
     * Takes the provided byte array and converts it into a hexidecimal string
     * with two characters per byte.
     */
    public static String bytesToHex(byte[] bytes)
    {
        return bytesToHex(bytes, false);
    }

    /**
     * Takes the provided byte array and converts it into a hexidecimal string
     * with two characters per byte.
     * 
     * @param withSpaces
     *            if true, this will put a space character between each
     *            hex-rendered
     *            byte, for readability.
     */
    public static String bytesToHex(byte[] bytes, boolean withSpaces)
    {
        StringBuilder sb = new StringBuilder();
        for (byte hashByte : bytes)
        {
            int intVal = 0xff & hashByte;
            if (intVal < 0x10)
            {
                sb.append('0');
            }
            sb.append(Integer.toHexString(intVal));
            if (withSpaces)
            {
                sb.append(' ');
            }
        }
        return sb.toString();
    }

    /**
     * Takes a string in hexidecimal format and converts it to a binary byte
     * array. This does no checking of the format of the input, so this should
     * only be used after confirming the format or origin of the string. The
     * input
     * string should only contain the hex data, two characters per byte.
     */
    public static byte[] hexToBytes(String hexString)
    {
        byte[] result = new byte[hexString.length() / 2];
        for (int i = 0; i < result.length; ++i)
        {
            int offset = i * 2;
            result[i] = (byte) Integer.parseInt(
                    hexString.substring(offset, offset + 2), 16);
        }
        return result;
    }

    /**
     * Encodes a string as a byte array using the default encoding.
     */
    public static byte[] stringToBytes(String string)
    {
        try
        {
            return stringToBytes(string, DEFAULT_CHARSET);
        }
        catch (UnsupportedEncodingException e)
        {
            throw new StringUtilException(DEFAULT_CHARSET + " not supported", e);
        }
    }

    /**
     * Encodes a string as a byte array with a specified character set encoding.
     */
    public static byte[] stringToBytes(String string, String charSet)
            throws UnsupportedEncodingException
    {
        if (string == null)
        {
            return null;
        }
        return string.getBytes(charSet);
    }

    /**
     * Decodes a byte array as a string using the default encoding (UTF-8)
     */
    public static String bytesToString(byte[] bytes)
    {
        try
        {
            return bytesToString(bytes, DEFAULT_CHARSET);
        }
        catch (UnsupportedEncodingException e)
        {
            throw new StringUtilException(DEFAULT_CHARSET + " not supported", e);
        }
    }

    /**
     * Decodes a byte array as a string using the specified character set
     * encoding.
     */
    public static String bytesToString(byte[] bytes, String charSet)
            throws UnsupportedEncodingException
    {
        if (bytes == null)
        {
            return null;
        }
        return new String(bytes, charSet);
    }

    /**
     * Returns an MD5 checksum of the provided array of bytes.
     */
    public static byte[] hash(byte[] body)
    {
        try
        {
            return MessageDigest.getInstance(HASH_ALGORITHM).digest(body);
        }
        catch (NoSuchAlgorithmException e)
        {
            throw new StringUtilException(HASH_ALGORITHM + " not supported", e);
        }
    }

    /**
     * Returns an MD5 checksum of the data read from the specified InputStream.
     */
    public static byte[] hash(InputStream data) throws IOException
    {
        try
        {
            MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM);
            byte[] buf = new byte[4096];
            int len;
            while ((len = data.read(buf)) != -1)
            {
                digest.update(buf, 0, len);
            }
            return digest.digest();
        }
        catch (NoSuchAlgorithmException e)
        {
            throw new StringUtilException(HASH_ALGORITHM + " not supported", e);
        }
    }

    /**
     * Returns an MD5 checksum of the provided string, which is encoded into
     * UTF-8
     * format first for unambiguous hashing.
     */
    public static byte[] hash(String content)
    {
        return hash(stringToBytes(content));
    }

    /**
     * Return the suffix of the passed file name.
     * 
     * @param fileName
     *            File name to retrieve suffix for.
     * 
     * @return Suffix for <TT>fileName</TT> or an empty string
     *         if unable to get the suffix.
     * 
     */
    public static String getFileNameSuffix(String fileName)
    {
        if (fileName == null)
            return "";

        final int pos = fileName.lastIndexOf('.');
        if (pos > 0 && pos < fileName.length() - 1)
        {
            return fileName.substring(pos + 1);
        }
        return "";
    }
  
    public static boolean isPunctuation(char ch)
    {
    	for (int i = 0; i < PUNCTS.length; i++)
		{
			if (PUNCTS[i] == ch)
			{
				return true;
			}
		}
    	return false;
    }
    
	static char[]			PUNCTS							= new char[] { 
		0x0021, 0x0024, 0x0025, 0x0028, 0x0029, 0x002C, 0x002E, 0x003A, 0x003B, 0x003E, 
		0x003F, 0x005B, 0x005D, 0x007B, 0x007D, 0x00A2, 0x00A3, 0x00A5, 0x00A8, 0x00B0, 
		0x00B7, 0x02C7, 0x02C9,//23
		
		0x2013, 0x2014, 0x2015, 0x2016, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2025, 
		0x2026, 0x2027, 0x2030, 0x2032, 0x2033, 0x2035, 0x203A, 0x2103, 0x2236, 0x2574, //43
		
		0x3001, 0x3002, 0x3003, 0x3008, 0x3009, 0x300A, 0x300B, 0x300C, 0x300D, 0x300E, 
		0x300F, 0x3010, 0x3011, 0x3014, 0x3015, 0x3016, 0x3017, 0x301D, 0x301E, //62
		
		0xFE30, 0xFE31, 0xFE33, 0xFE34, 0xFE35, 0xFE36, 0xFE37, 0xFE38, 0xFE39, 0xFE3A, 
		0xFE3B, 0xFE3C, 0xFE3D, 0xFE3E, 0xFE3F, 0xFE40, 0xFE41, 0xFE42, 0xFE43, 0xFE44, 
		0xFE4F, 0xFE50, 0xFE51, 0xFE52, 0xFE54, 0xFE59, 0xFE5A, 0xFE5B, 0xFE5C, 0xFE5D, 
		0xFE5E, //93
		
		0xFF01, 0xFF02, 0xFF04, 0xFF05, 0xFF07, 0xFF08, 0xFF09, 0xFF0C, 0xFF0E, 0xFF1A, 
		0xFF1B, 0xFF1F, 0xFF3B, 0xFF3D, 0xFF40, 0xFF5B, 0xFF5C, 0xFF5D, 0xFF5E, 0xFF64, 
		0xFFE0, 0xFFE1, 0xFFE5, //116
		
		'@', '~', '#', '^', '&', '_', '+', '-', '=', '\'', '€', '﹉', //加12个输入的符号
		  };
}
